using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;

namespace Terra.Data.Extensions;

/// <summary>
/// IQueryable扩展方法
/// </summary>
public static class QueryableExtensions
{
    /// <summary>
    /// 动态排序
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">查询对象</param>
    /// <param name="propertyName">属性名</param>
    /// <param name="isDescending">是否降序</param>
    /// <returns>排序后的查询对象</returns>
    public static IQueryable<T> ApplyOrder<T>(
        this IQueryable<T> query,
        string propertyName,
        bool isDescending = false)
    {
        if (string.IsNullOrWhiteSpace(propertyName))
        {
            return query;
        }

        // 获取属性信息
        var propertyInfo = typeof(T).GetProperty(
            propertyName,
            BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        if (propertyInfo == null)
        {
            return query;
        }

        // 创建参数表达式 (x => x.PropertyName)
        var parameter = Expression.Parameter(typeof(T), "x");
        var property = Expression.Property(parameter, propertyInfo);
        var lambda = Expression.Lambda(property, parameter);

        // 构建排序方法
        var methodName = isDescending ? "OrderByDescending" : "OrderBy";
        var genericMethod = typeof(Queryable)
            .GetMethods()
            .First(m => m.Name == methodName && m.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(T), propertyInfo.PropertyType);

        // 应用排序
        return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, lambda });
    }

    /// <summary>
    /// 应用分页
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">查询对象</param>
    /// <param name="pageIndex">页码</param>
    /// <param name="pageSize">每页大小</param>
    /// <returns>分页后的查询对象</returns>
    public static IQueryable<T> ApplyPaging<T>(
        this IQueryable<T> query,
        int pageIndex,
        int pageSize)
    {
        if (pageIndex < 1)
        {
            pageIndex = 1;
        }

        if (pageSize < 1)
        {
            pageSize = 10;
        }

        return query
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize);
    }

    /// <summary>
    /// 应用软删除过滤
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">查询对象</param>
    /// <returns>过滤后的查询对象</returns>
    public static IQueryable<T> ApplySoftDelete<T>(this IQueryable<T> query)
        where T : class
    {
        var property = typeof(T).GetProperty("IsDeleted");
        if (property == null)
        {
            return query;
        }

        var parameter = Expression.Parameter(typeof(T), "x");
        var propertyAccess = Expression.Property(parameter, property);
        var condition = Expression.Equal(propertyAccess, Expression.Constant(false));
        var lambda = Expression.Lambda<Func<T, bool>>(condition, parameter);

        return query.Where(lambda);
    }

    /// <summary>
    /// 应用租户过滤
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">查询对象</param>
    /// <param name="tenantId">租户ID</param>
    /// <returns>过滤后的查询对象</returns>
    public static IQueryable<T> ApplyTenantFilter<T>(this IQueryable<T> query, Guid? tenantId)
        where T : class
    {
        if (!tenantId.HasValue)
        {
            return query;
        }

        var property = typeof(T).GetProperty("TenantId");
        if (property == null)
        {
            return query;
        }

        var parameter = Expression.Parameter(typeof(T), "x");
        var propertyAccess = Expression.Property(parameter, property);
        var condition = Expression.Equal(propertyAccess, Expression.Constant(tenantId.Value));
        var lambda = Expression.Lambda<Func<T, bool>>(condition, parameter);

        return query.Where(lambda);
    }

    /// <summary>
    /// 应用关键字搜索
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">查询对象</param>
    /// <param name="keyword">关键字</param>
    /// <param name="propertyNames">要搜索的属性名数组</param>
    /// <returns>过滤后的查询对象</returns>
    public static IQueryable<T> ApplyKeywordSearch<T>(
        this IQueryable<T> query,
        string keyword,
        params string[] propertyNames)
        where T : class
    {
        if (string.IsNullOrWhiteSpace(keyword) || propertyNames == null || !propertyNames.Any())
        {
            return query;
        }

        var parameter = Expression.Parameter(typeof(T), "x");
        Expression combinedExpression = null;

        foreach (var propertyName in propertyNames)
        {
            var property = typeof(T).GetProperty(propertyName);
            if (property == null || property.PropertyType != typeof(string))
            {
                continue;
            }

            var propertyAccess = Expression.Property(parameter, property);
            var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            var searchValue = Expression.Constant(keyword);
            var containsExpression = Expression.Call(propertyAccess, containsMethod, searchValue);

            combinedExpression = combinedExpression == null
                ? containsExpression
                : Expression.OrElse(combinedExpression, containsExpression);
        }

        if (combinedExpression != null)
        {
            var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
            return query.Where(lambda);
        }

        return query;
    }
}